home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 3 / Gold Medal Software - Volume 3 (Gold Medal) (1994).iso / graphics / 3dvect30.arj / PMODE.DOC < prev    next >
Text File  |  1993-11-18  |  48KB  |  925 lines

  1. Warning:
  2.   This document is intended for the experienced programmer with knowledge of
  3. a good deal of assembly and the 386. If you are not one such person, the text
  4. that follows will be very confusing to you. Read on if you wish... You have
  5. been warned.
  6.  
  7. ------------------------------------------------------------------------------
  8. Some crap:
  9.  
  10.   This is the doc for PMODE v1.29a. I am not going to rewrite it just for
  11. version 2.1232 since almost all of this doc applies perfectly. There are a few
  12. changes from PMODE 1.29a to 2.1232 however. They follow at the end of this
  13. doc.
  14.  
  15.   This code, as well as this doc (damn I hate writing dox), were written up
  16. by Tran (a.k.a. Thomas Pytel) of Renaissance. It is intended for all those of
  17. you out there who would like to code in protected mode, but keep running up
  18. against obstacles. If you want to use this thing as it is though, you will
  19. have to code in 100% ASM. But hey, that's no problem. I do it all the time. I
  20. did write this thing for myself. So if you have a problem with it, tough. You
  21. have a few options. You can use it as it is, and code in all ASM (not a
  22. terribly difficult feat). You can examine the code, and use what you learn to
  23. code up your own protected mode system, one that you can maybe throw into a
  24. nice high level language maybe. Or you can just ignore this code and this doc.
  25.  
  26. ------------------------------------------------------------------------------
  27. Intro:
  28.  
  29.   Ah... many are the joys and woes of this chip we all know as the 386. The
  30. 486 - bah, just a 386 with a cache. The 586 - hahaha, two 486s with double the
  31. cache. But the wonderful 386 architecture. That finally gives us our needed
  32. flat memory. Not to mention the paging (not a personal favorite, but VERY
  33. useful). It's been around here for a while now, but it hasn't been used very
  34. much. I'm assuming you know all the problems associated with sharing the
  35. 386 protected mode amongst different programs. So I'll just go on to tell ya
  36. about two methods of doing this, VCPI and DPMI.
  37.  
  38.   In the beginning (or at least a long time ago). There was the EMS and the
  39. XMS. Two pathetic attempts by the hopelessly crippled (design-wise) 8086s and
  40. 286s. EMS and XMS were fine for accessing small chunks of a larger address
  41. space at a time. These methods were usually very slow. As for XMS, the 286
  42. actually had to copy memory every time it was requested. And hardware EMS
  43. resided on the slow system bus (well, not that much slower compared to the
  44. speed of those machines). With the introduction of the 386, and soon the
  45. memory managers, things changed. EMS and XMS could now be handled with the
  46. on-chip paging mechanism. Which was faster than the memory copy method, and
  47. even the hardware EMS. But for this task, the memory managers had to run the
  48. system in V86 mode. Forsaking all other programs which would want to take
  49. control of the 386 protected mode.
  50.  
  51.   Thus was devised VCPI. A method of allowing programs running in the V86
  52. system to switch to protected mode. After switching to protected mode, those
  53. programs would have total control of the system. Running at a privilege level
  54. of 0. They could communicate with the other protected mode program, the VCPI
  55. server, through a common memory address space starting with the first megabyte
  56. of memory. VCPI was a superset of the EMS standard, and was implemented in
  57. virtually all memory managers and 386 extenders. But VCPI was woefully
  58. inadequate for the multitaskers that were soon to follow.
  59.  
  60.   Then there came forth the DPMI standard. Born from the window, it is the
  61. epitome of total lameness. Many are the unknowing peons who run the window.
  62. And many are the lamer who do not know the purpose of our favorite key
  63. combination, the CTRL+ALT+DEL. A wondrous function of that eternal TSR called
  64. the BIOS. Many a fool activate it whilst on a local BBS, erroneously believing
  65. they will get free downloads. But I digress, we were all lame at one time or
  66. another. This standard of the DPMI was different from those of antiquity.
  67. This foul interface was designed with total control in mind. Total control
  68. by the evil host whose lameness was imposed upon all those running beneath it.
  69. At a lowly CPL3, this loathsome beast's clients are truly at a disadvantage.
  70. Their INs and OUTs under the thorough scrutiny of the master. Forced to
  71. grovel for precious memory. Not even their own instructions are sacred. For
  72. the fiend which is DPMI, patiently waits, and watches. Ready to tear the
  73. precious life from the pitiable client at the slightest sign of the forbidden
  74. CLI. Then the monster does its deeds, toggling an illusion of that which was
  75. once real. This virtual flag of interrupts ensures the depraved host total
  76. domination. So easy to hate it is this beast. But yet... In the name of
  77. multiple tasking, and system integrity. The DPMI continues to gain ground.
  78. (Well sure, if ya want those things. But where is your adventure... Your
  79. spirit... Have you totally forgotten what a good crash feels like???)
  80.  
  81.   These days, VCPI and DPMI permeate the world of 386 software. From memory
  82. managers, to Windows and OS/2. If something is protected, its using one of
  83. these. VCPI though, is on its way out. Being replaced by DPMI. And if you want
  84. to code protected mode... Unless you force upon the user a clean boot, you
  85. have to support one of these two standards.
  86.  
  87. ------------------------------------------------------------------------------
  88. So what you got here:
  89.  
  90.   Having coded raw flat protected mode, I am hooked. I shall not revert to
  91. real mode limitations. My previous protected mode system 'START32' was very
  92. intolerant of crap like VCPI and DPMI and EMS and XMS and everything else. It
  93. required a clean boot to function. But hey, for that I got pure wonderful
  94. flat protected mode. Unfortunately, the masses did not share my sentiment.
  95. And they whined about rebooting. Pitiful as they were, I have heard their
  96. cries. And I understand. The culmination is the code before you (or rather in
  97. the other file, PMODE.ASM). This is basically the same pure beautiful flat
  98. protected mode system as my old START32 system, except that it will run under
  99. VCPI and DPMI and XMS (albeit a little slower).
  100.  
  101. ------------------------------------------------------------------------------
  102. What it thinks it does:
  103.  
  104.   The point of this protected mode header thingy is to provide a simple, flat
  105. mode environment for easy assembly coding. PMODE will take care of detecting
  106. a 386, the type of system (raw,XMS,VCPI,or DPMI) and making sure there is
  107. enough memory, both low and high. The protected mode code runs in one big
  108. segment, the size of which is infinite (or 4GB, whichever is larger). You can
  109. call real mode interrupts of far routines from your pmode code. And those
  110. in turn can call back to protected mode. And on and on like this to the limits
  111. of the stack. Once in pmode, all normal IRQs are still active and are by
  112. default, redirected to their real mode handlers. You can intercept these
  113. IRQs for your own use, then pass them on to the real mode handlers or not.
  114.  
  115.   This system was coded with speed in mind. It is meant for stuff like games,
  116. demos (as in the ones with cool muzik and grafix), or stuff that needs a lot
  117. of memory. What you should not attempt from this system, is going TSR, or
  118. executing DOS programs. This system runs absolutely perfectly for what it was
  119. meant for in raw and XMS. There are some minor problems under VCPI and DPMI
  120. though, and i'll list them later. But right now...
  121.  
  122. ------------------------------------------------------------------------------
  123. First of all, the structure:
  124.  
  125.   In PMODE.ASM, there are three segments. They are as follows:
  126.  
  127. ) CODE16 - A 16bit segment that holds all the real mode and 16bit protected
  128.            mode init and exit code. I'm not sure (I've forgotten by now), but
  129.            you should probably leave it as the first segment of the EXE.
  130. ) CODE32 - The huge 32bit segment. You can throw in as much code as will fit
  131.            in low memory (no 64k fixup overflows). If you need more code
  132.            space, you're gonna have to load it into extended memory at runtime
  133.            and use it there. In pmode, addresses (code and data, they're the
  134.            same memory space) are offset from the beginning of this segment.
  135.            If you want to address something below the beginning of this seg,
  136.            you go about it in a different way. I'll explain later.
  137. ) CODEEND - This MUST be the last segment in the EXE. It is the base for the
  138.             stack and low memory allocation.
  139.  
  140.   The stack is an interesting animal (or was it a mineral¿). But anyway, you
  141. don't have to worry about the stack. The header will set it up to whatever
  142. you specify (up to a limit of 64k (there's a reason)). The same stack is
  143. shared by your pmode code and real mode calls (that's the reason). The stack
  144. always begins at CODEEND, and goes on for STAKLEN (a var declared at the top
  145. of PMODE.ASM) paragraphs. I will explain how its used by both pmode and real
  146. mode code later.
  147.  
  148.   There are two heaps you can allocate memory from at run time. Low memory,
  149. which is all conventional memory below A0000h. And extended memory, all the
  150. nice crap above 1M. There's a reason for keeping them separate (other than
  151. that big hole between A0000h and 100000h (which I did not want to fill in or
  152. rearrange with paging)). DOS can only see the low heap. So in calls where you
  153. have to pass buffer addresses, you must pass only buffers that you have in low
  154. memory. Low memory is the place where you would allocate any critical disk or
  155. DMA buffers (more on a few potential problems with DMA later).
  156.  
  157.   You call real mode interrupts and far routines with 'virtual registers'.
  158. Memory images of the registers as they will be set for the real mode int or
  159. proc. Code in real mode can call protected mode routines with those same
  160. 'virtual registers'.
  161.  
  162.   This thing has a few 'functions' for dealing with low and high memory and
  163. getting and setting IRQs. There are also some variables made available to ya
  164. which you'll find quite useful.
  165.  
  166. ------------------------------------------------------------------------------
  167. Details of runtime:
  168.  
  169.   I'm assuming you know all about selectors and descriptor tables and stuff
  170. like that. But going on... After it does all that it needs to, PMODE will
  171. jump to an external label in code32 called '_main'. This is where your 32bit
  172. code takes over. When you gain control:
  173.  
  174. ) The stack is all set up.
  175. ) The interrupts are disabled (and have been all the way from real mode) just
  176.   in case theres something you want to do first (like shut them all off at
  177.   21h and 0a1h, or replace all the vektorz (so what if I spell it vektorz)).
  178. ) CS points to the code segment you're running in (duh...).
  179. ) DS,ES,FS, and SS point to an alias of the code segment (same memory). (In
  180.   case you don't remember, you can NEVER write to a CS: override in protected
  181.   mode... Read, yes... But not write).
  182. ) GS is a segment that's just as big as the others (infinite, remember?...)
  183.   but starts at absolute 0. This is ofcourse useful for accessing the real
  184.   mode int vektor table, or the BIOS data area, or the PSP, etc...
  185.  
  186.  
  187. Selectors:
  188.  
  189.   There are three main selectors you have to know. '_selcode', '_seldata', and
  190. '_selzero' are 16bit word vars you can access to get the selector values for
  191. the code, data, and zero (GS) segments respectively. Their values are 8, 10h,
  192. and 18h under all protected systems except DPMI. As I said, on getting to
  193. _main, CS=_selcode, DS=ES=FS=SS=_seldata, and GS=_selzero. You can change the
  194. segregs if you wish (for example to do a REP MOVS in the zero seg). But the
  195. PMODE 'functions' and ints expect the segregs to be these values (except for
  196. the special case of SS, this will be explained later). And these must be the
  197. values when you jump to '_exit' to return to DOS. Another thing that is
  198. assumed by PMODE is DF=0 (direction flag is clear (like the CLD instruction)).
  199. This is because most string moves are forward. If you want to do a string move
  200. the other way, go ahead. Just do a CLD after.
  201.  
  202.  
  203. Linear addresses:
  204.  
  205.   As I said, in pmode, all addresses are relative to the beginning of the
  206. CODE32 segment (which could start anywhere in low memory). For this reason,
  207. you must adjust any physical memory pointers before you use them. That is,
  208. to access something at B8000h (B800:0000, if you haven't noticed, I'm using
  209. all true linear addresses in this doc, no need for the seg:off crap). Anyway,
  210. If you want to write to B8000h, it will not be at DS:B8000, but at
  211. DS:(B8000h-linear address of the beginning of CODE32). And this linear address
  212. is stored in a variable called '_code32a'. So if the segment CODE32 was 1F43,
  213. the linear address would be 1F430h (seg*10h remember¿). So to get a pointer
  214. to B8000h, you would do something like:
  215.  
  216.   mov eax,0b8000h
  217.   sub eax,_code32a
  218.  
  219.   And just this macro is provided in PMODE.INC, as well as a macro to go the
  220. other way, relative address to physical. Ofcourse, if you address something
  221. with GS (assuming GS is _selzero), you can use the actual unadjusted linear
  222. address. The absolute linear address for CODE16 is also provided in
  223. '_code16a'. As well as the absolute linear address of the PSP in '_pspa'. The
  224. linear addresses '_code16a' and '_pspa' will always be less than '_code32a'.
  225. To access them (memory pointed to by them, these vars are in CODE32), you will
  226. have to use one of two methods. One is easy enough, just use the GS segment.
  227. Or you could use negative indexes from the normal segment. This relies on the
  228. 4G wraparound (don't worry, the limits of all segments in the descriptor table
  229. are 4G). Strange things may happen if the 686 doesn't support the 4G wrap, but
  230. from what I understand, the 586 is still limited (limited??? damn it, even CDs
  231. don't reach that) to 4G segments.
  232.  
  233.  
  234. Memory (dis)organization:
  235.  
  236.   As for the memory. You have those two heaps, low and high (extended) memory.
  237. Each of which is guaranteed to be at least as much as you specified in LOWMIN
  238. and EXTMIN at the top of PMODE.ASM. PMODE will hog up all low memory (because
  239. its meant to run standalone), and it will attempt to grab all the high memory
  240. it can. Two dwords hold information about each memory area. '_lomembase' and
  241. '_lomemtop' specify the base and top of the low memory pool as relative
  242. addresses (ready to use, no adjustment needed). The total amount of low memory
  243. available in bytes is _lomemtop-_lomembase (notice _lomemtop points to one
  244. byte beyond the last available byte). The _getlomem 'function' is a very
  245. simple routine that takes a length in EAX, and checks to see if there is
  246. enough low mem. If there is enough, it adds the length to _lomembase and
  247. returns a pointer (ready to use) in EAX to the low memory block along with the
  248. carry flag clear. If it finds that _lomemtop-_lomembase > length (not enough
  249. memory), it returns with the carry flag set and EAX undefined. '_himembase',
  250. '_himemtop', and '_gethimem' are the same thing for high mem as those other
  251. things are for low mem. Just to make sure you understand this, here is some
  252. code, cuz as we all know, code speaks louder than words (actually, silence
  253. speaks louder than code, but what the hell).
  254.  
  255.   xor al,al
  256.   mov edi,_lomembase            ; fill all available low memory with 0
  257.   mov ecx,_lomemtop
  258.   sub ecx,edi
  259.   rep stosb
  260.   mov edi,_himembase            ; ditto for high memory
  261.   mov ecx,_himemtop
  262.   sub ecx,edi
  263.   rep stosb
  264.  
  265.   There is one other curiosity provided. '_getmem' will get a any block of
  266. memory. It will first check low memory, and if there is not enough, it will
  267. check high memory. If you wish, you can code yourself a little 'malloc'
  268. library that will deal with blocks, and provide you with all the joys of
  269. fragmentation.
  270.  
  271.  
  272. Calling icky real mode:
  273.  
  274.   I did say you can call real mode, and back. Let me first say that this is
  275. only provided so that you can call real mode interrupts, and routines that
  276. you don't want to recode in protected mode. I would not suggest making it a
  277. habit of coding across modes. Except maybe if you do a driver that you also
  278. want to work from real mode. But anyway... You can call real mode interrupts
  279. or procedures from protected mode through INT 32h (call real mode far proc)
  280. and INT 33h (call real mode int). These interrupts are only available to the
  281. protected mode part of your program. In real mode, there is a separate INT 32h
  282. that calls a protected mode routine. Don't confuse the two INT32s with each
  283. other, though they do basically the same thing. To pass register values across
  284. modes, you use 'virtual registers' (I just love that word... virtual... haha).
  285. These 'virtual registers' are merely memory images of EAX,EBX,ECX,EDX,ESI,EDI,
  286. EBP,DS,ES,FS,and GS. AL and AH and AX and BL ... etc ... are there too, and
  287. they share the appropriate memory space with each other so if you change the
  288. 'virtual' AH register, the 'virtual' AX and EAX registers will be changed
  289. accordingly. You'll notice there are no SS,ESP,CS,EIP registers. CS:EIP is
  290. taken from the real mode interrupt vektor table for int calls, and passed in
  291. the real CX:DX registers for a procedure call. SS:ESP is set up in the master
  292. stack used by PMODE. Which is the stack your program runs on. I'll explain the
  293. stack handling in detail l8r. Heres a breakdown of the ints:
  294.  
  295. ) INT 33h from pmode: Do a real mode interrupt.
  296.   AL=interrupt you want to do. All V86R_??? general and segment registers will
  297.   be passed to the real mode handler. They will also be passed back as the
  298.   return values. The carry, zero, aux, parity, sign, and overflow flags will
  299.   be passed back as the actual CPU flags. The real mode interrupt will be
  300.   called with interrupts disabled (as it is usually). Keep in mind, no CPU
  301.   registers will be modified (except the flags mentioned). Only their V86R_???
  302.   images will be changed by the real mode int handler.
  303.  
  304. ) INT 32h from pmode: Call a real mode far procedure.
  305.   CX:DX=seg:off you want to call. The register passing works just like INT33.
  306.   Except that the interrupt flag will be preserved across the call to real
  307.   mode (but not back, the IF flag will be in the same state as it was before
  308.   the int).
  309.  
  310. ) INT 32h from real mode: Call a pmode procedure.
  311.   EDX=off. A 32bit offset in the CODE32 segment. The register passing works
  312.   just like for the other INT32 in pmode, except that segregs are not passed
  313.   to or from the pmode routine. Upon entry to the routine, the system standard
  314.   thingys are set. That is:
  315.     CS=_selcode, DS=ES=FS=SS=_seldata, GS=_selzero, DF=0 (CLD).
  316.   And they must be that when the thing executes its RET (not RETF).
  317.  
  318.  
  319. DPMI takes its toll and IRQs:
  320.  
  321.   Upon startup, all the interrupt vektors for IRQs point to routines that
  322. redirect the IRQs to their default real mode handlers. You can hook into any
  323. IRQ you want. There are two dword pointers that allow you to get and set IRQ
  324. vektorz. '_getirqvect' and '_setirqvect' point to routines to get and set the
  325. linear address of the handler for specific IRQs within the CODE32 segment. To
  326. get the address of a handler, just do a 'call _getirqvect' with BL set to the
  327. IRQ num you want (0-15). EDX will be returned pointing to its current handler.
  328. To set an IRQ, pass BL again as the IRQ number, and EDX as the offset of the
  329. new handler. You can chain to the old handler if you want just by jumping to
  330. the old address when your handler is done processing.
  331.  
  332.   When your IRQ handler is called, you can be sure of only one thing. The IF
  333. flag is clear. All the general regs and segregs should be treated as
  334. undefined. It would seem logical that if SS=_seldata throughout the entire
  335. code, it will be that in the IRQ. And this is true under all systems except
  336. DPMI. DPMI deems it fit to switch onto another stack. And that's the stack
  337. your handler must be on when it does its IRETD. You can switch stacks during
  338. processing if you want, but I really don't suggest doing that. Also, if you
  339. intend to call INT32/33 from your handler (you can, they are all totally
  340. reenterant), you must be on that stack (DPMI requirement of a mode switch it
  341. seems. I've tried to switch onto another stack (yes locked) and switch to
  342. real mode (using DPMI raw mode switching and state saving) but DPMI dies if
  343. another IRQ goes off in real mode).
  344.  
  345.   Another consideration for DPMI is the IF flag. According to DPMI specs, only
  346. CLI, STI, and INT 31h functions AX=900h and AX=901h should be counted on to
  347. modify the interrupt flag (POPF(D) and IRET(D) should not). This is because
  348. certain DPMI systems might have to virtualize the interrupt flag, and keep the
  349. real flag enabled at all times (but don't worry, if the 'virtual' flag is
  350. clear, your program will not get any IRQs). In practice, certain DPMIs do
  351. allow IRET(D)s and POPF(D)s to modify the virtual interrupt flag. But this is
  352. inconsistent across them. So if you want DPMI compatibility (you probably do,
  353. or you would not be using this code), you should follow these rules:
  354.  
  355. ) CLI and STI are allowed, and do their functions.
  356. ) Don't assume anything about POPF(D) and IRET(D) and the interrupt flag.
  357. ) Don't assume the interrupt flag PUSHF(D) stores on the stack is correct,
  358.   it might be the real flag or the 'virtual' flag.
  359. ) These DPMI INT 31h functions are supported under all systems.
  360.   ) AX=900h: Get state of IF and disable it. Returns AL set to the IF flag.
  361.   ) AX=901h: Get state of IF and enable it. Returns AL set to the IF flag.
  362.   ) AX=902h: Only returns AL set to the IF flag (0=disabled, 1=enabled).
  363. ) At the end of an IRQ handler, put a STI. When the handler is called, flags
  364.   are automatically disabled. And if you do not reenable them, and neither
  365.   does the IRETD... Well... you get the point.
  366.  
  367.  
  368. The stack entity:
  369.  
  370.   PMODE uses one major stack for both pmode and real mode. This stack is
  371. always located in low memory (always locked under DPMI). The size of the stack
  372. is set at the top of PMODE.ASM in the var 'STAKLEN'. There is another var
  373. there called 'STAKSAFE', and this is what must be explained.
  374.  
  375.   When a mode switch occurs, the new stack is the old stack base minus
  376. STAKSAFE paragraphs. The stack base is the stack location when your program
  377. starts. And it is only modified by mode switches. An example is in order here.
  378.  
  379.   Say your program starts, and you have a STAKLEN of 100h (1000h bytes) and
  380. a STAKSAFE of 20h (200h bytes). After you have pushed a few values, and are
  381. about to call real mode, the stack has gone down to 0F30h. After the mode
  382. switch, the location in the stack will be 0E00h (1000h-200h). Now in real mode
  383. you push some values. And the stack goes down to 0DE0h. Then you make a call
  384. to protected mode. The protected mode stack will start at 0C00h (0E00h-200h).
  385. After the return to real mode, the stack will be back to 0DE0h. You pop your
  386. values and return again. Back in protected mode, the stack will be where it
  387. was before the initial call to real mode, at 0F30h. So STAKSAFE is the maximum
  388. stack size that is safe from modification if a mode switch occurs. But beware,
  389. an IRQ that goes off that is redirected to the other mode is a mode switch.
  390. That is, an IRQ in protected mode that is redirected to real mode will cause
  391. the stack change. As will ANY IRQ in real mode (since it temporarily goes to
  392. pmode, and is redirected back to real mode). An IRQ that occurs in pmode, and
  393. is NOT sent to real mode is NOT a mode switch.
  394.  
  395.   Under DPMI things are slightly different. DPMI handles its own stack
  396. switching. Any IRQ causes a switch to a totally different stack. It gets a
  397. little complicated as DPMI does its switching among 4 different stacks. I
  398. was not able to put in my own IRQ redirectors to real mode, so you have to
  399. rely on DPMIs redirectors (which in some cases don't redirect the IRQs as they
  400. should). It seems even DPMI has problems managing its own stacks. Perhaps I
  401. missed some critical little point. But I don't think so... Even with state
  402. saving and the raw mode switching, if I switched off the stack the DPMI host
  403. provided for an IRQ, then jumped to real mode, and another IRQ occurred...
  404. Well... Let's just say that was the end of that. This may be a little
  405. confusing, so let me summarize what will keep you safe:
  406.  
  407. ) In an IRQ handler, DON'T switch off the stack it is entered with. Which is
  408. not guaranteed that SS=_seldata.
  409. ) Don't do more nested calls across modes than (STAKLEN/STAKSAFE)-1. (-2 if
  410. you just want to be totally safe).
  411. ) You CAN safely assume SS=_seldata in protected mode only in your main stream
  412. of execution, and in routines that are called with INT32 from real mode.
  413. ) Consider your maximum effective stack size to be STAKSAFE, not STAKLEN.
  414. ) You CAN call across modes using INT32/33 from an IRQ handler in both real
  415. and protected mode (useful for that pesky mouse callback thingy).
  416.  
  417.  
  418. Exceptions:
  419.  
  420.   Are handled entirely by the DPMI host in those systems. In raw, XMS, and
  421. VCPI, exceptions 0, 1, 2, 5, and 7 are reflected to real mode just like IRQs
  422. would be (they are sent safely (actual real mode)). Exceptions 3, 4, 8 and
  423. 9-1fh cause immediate termination. There is no debug dump done. If you want
  424. though, you can put in your own. The main exception handler is 'cp_exc'
  425. somewhere around line 1828 in PMODE.ASM. As you can see from the little entry
  426. code above it, it is entered directly from an exception with all registers
  427. pushed and AL=exception number. And as you can see all it does is load up the
  428. system default thingies and jumps to _exit. Just to clarify, it loads up DS,
  429. ES, and FS with 10h and not _seldata simply because this is an exception
  430. handler only for raw, XMS, and VCPI. Under which _seldata always equals 10h.
  431.  
  432. ------------------------------------------------------------------------------
  433. The 'virtual' registers might be a little confusing:
  434.  
  435.   So here's some semi-sorta-pseudo-code.
  436.  
  437. all_genregs     = [EAX, EBX, ECX, EDX, ESI, EDI, EBP]
  438. all_segregs     = [DS, ES, FS, GS]
  439. all_regs        = [all_genregs, all_segregs]
  440. all_v_genregs   = [V86R_EAX, V86R_EBX, V86R_ECX, V86R_EDX,
  441.                    V86R_ESI, V86R_EDI, V86R_EBP]
  442. all_v_segregs   = [V86R_DS, V86R_ES, V86R_FS, V86R_GS]
  443. all_v_regs      = [all_v_genregs, all_v_segregs]
  444. IF_stat         = current interrupt flag status
  445. pre-int_IF_stat = interrupt flag status before the software INT 32/33
  446. other_IF_stat   = interrupt status to set for the procedure/int called
  447.  
  448. INT 32h from protected mode:
  449.   PUSH  all_genregs
  450.   PUSHF                         ; just for show, they are assumed to affect
  451.   CLI                           ; the interrupt flag.
  452.   MOV   other_IF_stat,pre-int_IF_stat
  453.   JMP   realmode
  454. backtopmode:
  455.   MOV   DS,_seldata
  456.   MOV   ES,_seldata
  457.   MOV   FS,_seldata
  458.   MOV   GS,_selzero
  459.   POPF
  460.   POP   all_genregs
  461.   IRETD
  462. realmode:
  463.   MOV   all_regs,all_v_regs
  464.   MOV   IF_stat,other_IF_stat
  465.   CALL  procedure
  466.   CLI
  467.   MOV   all_v_regs,all_regs
  468.   JMP   backtopmode
  469.  
  470. INT 33h from protected mode:
  471.   PUSH  all_genregs
  472.   PUSHF
  473.   CLI
  474.   JMP   realmode
  475. backtopmode:
  476.   MOV   DS,_seldata
  477.   MOV   ES,_seldata
  478.   MOV   FS,_seldata
  479.   MOV   GS,_selzero
  480.   POPF
  481.   POP   all_genregs
  482.   IRETD
  483. realmode:
  484.   MOV   all_regs,all_v_regs
  485.   CALL  interrupt
  486.   CLI
  487.   MOV   all_v_regs,all_regs
  488.   JMP   backtopmode
  489.  
  490. INT 32h from real mode:
  491.   PUSH  all_regs
  492.   PUSHF
  493.   CLI
  494.   MOV   other_IF_stat,pre-int_IF_stat
  495.   JMP   pmode
  496. backtorealmode:
  497.   POPF
  498.   POP   all_regs
  499.   IRET
  500. pmode:
  501.   CLD
  502.   MOV   DS,_seldata
  503.   MOV   ES,_seldata
  504.   MOV   FS,_seldata
  505.   MOV   GS,_selzero
  506.   MOV   all_genregs,all_v_genregs
  507.   MOV   IF_stat,other_IF_stat
  508.   CALL  procedure
  509.   CLI
  510.   MOV   all_v_genregs,all_genregs
  511.   JMP   backtorealmode
  512.  
  513. ------------------------------------------------------------------------------
  514. Potential DMA problems:
  515.  
  516.   As you know, the DMA controllers in the PC use all physical addresses.
  517. Nothing but the processor itself knows how linear memory is arranged in the
  518. physical memory banks. When paging is disabled, the relationship is very
  519. simple. The linear address is always the same as the physical address. But
  520. when you enable paging, that could get all screwed up. In raw mode and XMS,
  521. you don't have to worry about this since paging is disabled. But under VCPI
  522. and DPMI things are different. You can almost definately count on extended
  523. memory addresses not being consistent with their physical addresses. Low
  524. memory however, will usually map perfectly to its physical addresses. Unless
  525. the program is running in some sort of multitasking system. Then the chances
  526. are slim. The point is that you can't trust DMA much under VCPI and DPMI.
  527.  
  528.   There is something called VDS (Virtual DMA Specification). This is the
  529. recommended way of handling DMA under VCPI and DPMI. I don't know too much
  530. about it now though. Maybe in the future I'll put something in based on that
  531. for DMA stuff. But for now your options are:
  532.  
  533. ) Don't use DMA (Not too hard, except if you wanna do SB output).
  534. ) Try to use DMA normally. It will work in raw and XMS, and most probably
  535. under VCPI and DPMI if they are not multitasking your program.
  536. ) If you know how to use VDS, feel free... (real mode int calls, remember¿)
  537.  
  538. ------------------------------------------------------------------------------
  539. And now to discuss some of the finer points of raw mode:
  540.  
  541.   Which is when this thing does not find anything that is running the system
  542. in V86 mode, and it can do all of its own protection control. This is the best
  543. possible way this thing can be run. The protected mode system runs at a CPL0.
  544. All IRQs and ints are handled through interrupt gates rather than task gates
  545. for speed. There is no task switching done at all, even to go to V86 mode.
  546. Paging is disabled to avoid that extra little bit of overhead. All IRQs are
  547. by default redirected to real mode (not true under some DPMIs). Actually real
  548. mode is V86 mode, still under the control of PMODE. So any IRQs that happen
  549. while a real mode thingy is being processed are taken immediately to protected
  550. mode. Where if you have a handler, it gets control right away. There were some
  551. problems with my old 'START32' code and the A20. I was not waiting for it to
  552. go stable, and was not testing to make sure. This has been fixed. If the A20
  553. fails to enable through the standard AT method or the PS2 method, PMODE will
  554. quit with an error message. All DMA in raw mode is real, since ALL linear
  555. addresses are the actual physical addresses because paging is disabled. The
  556. interrupt flag is real. When you disable interrupts, they are disabled, and
  557. will not screw up anything that might be timing sensitive as well as interrupt
  558. sensitive.
  559.  
  560.   So if you need to do something like time the vertical or horizontal retrace
  561. on your monitor, this is the mode you (or whoever) should be running in.
  562.  
  563. ------------------------------------------------------------------------------
  564. XMS:
  565.  
  566.   Is basically raw mode. Except that instead of INT 15h AH=88h, the XMS driver
  567. is used to allocate and lock extended memory. There is only one potential
  568. problem (and this goes for raw mode as well). If something tries to go to
  569. protected mode while in a real mode interrupt call, it will screw up.
  570. Obviously because the system is already in protected mode under the control of
  571. PMODE. This mode switch attempt would usually be the result of the XMS driver
  572. trying to copy memory for something. Or a disk cache that uses extended mem.
  573.  
  574. ------------------------------------------------------------------------------
  575. VCPI:
  576.  
  577.   This is actually a slightly worse mode to run in than DPMI. True that VCPI
  578. is also basically raw mode. The CPL is 0, and there is nothing scrutinizing
  579. the execution of your code. Paging is enabled, but that is a minor detail.
  580. The problem comes with the way VCPI compatibility works. To call a real mode
  581. interrupt or procedure, PMODE has to pass protected mode control back to the
  582. VCPI server. This comes out to one thing. IRQs that occur in a real mode call
  583. will NOT make it to your protected mode handler. I'm sorry, there is nothing
  584. I can do about this, it's just the way VCPI works. Yes it is possible to go
  585. V86 yourself to service the real mode call. Believe me, I've tried it. But the
  586. problem is that under VCPI systems, most of the real mode stuff (including
  587. DOS) are very dependant on EMS. And if control is not passed back to the VCPI
  588. server, EMS will not function. In fact, most memory managers require that the
  589. server watch for the execution of a certain interrupt from real mode, and
  590. intercept it. The actual interrupt vector in the real mode table might point
  591. into limbo. But as long as the VCPI server is in control, it will be handled
  592. properly. There are a few ways you could work around this:
  593.  
  594. ) Do the IRQ handler in real mode. That way, it will always be called no
  595. matter what is in control. But this seems to defeat the purpose of protected
  596. mode. And if this is a timing critical IRQ, you have a problem because passing
  597. control from a program (PMODE) to the VCPI server to execute the real mode
  598. IRQ callback takes a bit of time. Not a terrible amount, but it is a delay.
  599.  
  600. ) Do the IRQ handler in protected mode, and keep real mode calls to a minimum.
  601. For example, disable all but the critical IRQs to your program. And try to
  602. handle as many as you can in pmode. (You can read the keyboard direct from the
  603. hardware can't you. And you do know how to output FFh to A1h and FDh to 21h).
  604. But remember one thing... When you go to do a real mode call (DOS file call or
  605. something else you can't do yourself). Whatever the hardware cause of your IRQ
  606. will still be active. And if an IRQ occurs in real mode, and there is no real
  607. mode handler for it. Well, you know... So you either put in some valid real
  608. mode handler that may merely set a flag that you have an IRQ to service. Or
  609. disable the source of the IRQ (mask it off at 21h or A1h).
  610.  
  611.   VCPI also has the little problem of the possibility of inconsistent linear
  612. with physical addresses. Which means DMA is screwed. Generally speaking,
  613. unless the VCPI is coming from a multitasking thingy like DesqView, the low
  614. memory addresses will be accurate. As I said, I'm thinking of ways to solve
  615. this little problem. But for now, if you want to do something that requires
  616. DMA. If you know how to work VDS, you can try it with the real mode int calls.
  617. Or you can tell whoever is running your program, that if it doesn't work, to
  618. do a clean boot.
  619.  
  620. ------------------------------------------------------------------------------
  621. DPMI:
  622.  
  623.   Actually, DPMI is not that bad for what it was designed to be. It could be
  624. a little more consistent across its implementations, but oh well. I am a
  625. game/demo/speed freak however. I don't like the overhead imposed by the paging
  626. and CPL3 (in CPL3, certain instructions have to be emulated by software...
  627. Luckily they are not very common instructions). And multitasking in general
  628. is not that hot when you're trying to do a timing critical action game. But
  629. we're stuck with what we're stuck with. I figure running in real mode under
  630. DPMI is even slower than protected mode.
  631.  
  632.   One really annoying problem with DPMI is that current implementations are
  633. far from perfect. QDPMI 1.01 for example, dies when an IRQ occurs in a pmode
  634. call from a real mode IRQ. DPMI dox say this shouldn't happen, and it doesn't
  635. under Windows 3.1 DPMI implementation. Thus, QDPMI 1.01 is buggy. And who
  636. knows how many other DPMIs out there.
  637.  
  638.   For those of you who know DPMI. I am using the raw mode switch routine to
  639. do cross mode calls with INT32/33. I am ofcourse also saving the state on the
  640. stack. So it is perfectly reenterant. Just as a minor note, DPMI converts the
  641. environment segment in the PSP to a protected mode selector. I convert it back
  642. to the segment after going pmode with DPMI. But since DPMI needs the selector
  643. for the final return to real mode, I put it back then. But you can count on it
  644. being a real mode type segment.
  645.  
  646.   Hmm, another little problem is that I'm not sure how many DPMIs out there
  647. actually reflect IRQs to real mode if they occur in protected mode. Windows
  648. 3.1 seems to send them all over as it should. QDPMI however sends IRQ1, but
  649. not IRQ0. And it also doesn't seem to pass IRQs that occur in real mode
  650. through their protected mode handlers. Again, Windows 3.1 does. And from what
  651. I read in the dox, the Windows way is the way DPMI is supposed to be done.
  652.  
  653. ------------------------------------------------------------------------------
  654. Some misc notes:
  655.  
  656. ) Under VCPI, this thing will map as much extended memory as it can, up to
  657. 60M, without allowing the page tables to use up more memory than would leave
  658. LOWMIN. Allocating up to 60M means there is an absolute highest amount of
  659. extended memory under VCPI of 59M (even if there is more available).
  660.  
  661. ) Yes, this thing modifies its own code.
  662.  
  663. ) Before exiting your program, you do NOT need to restore any IRQ vektorz
  664. (pmode that is, if you modified the real mode vektor table, you gotta restore
  665. it). And you do not have to restore the IRQ masks at 21h and A1h (PMODE stores
  666. them before jumping to _main, and restores them before exiting).
  667.  
  668. ) If youre gonna add other segments (16bit right¿), put them in between CODE16
  669. and CODE32 only if they're small enough to still allow access to CODE32 data
  670. from CODE16. Otherwise put them between CODE32 and CODEEND. You can also just
  671. stick your 16bit code in a CODE16 segment.
  672.  
  673. ) The '_ret' label is provided simply because there are usually a lot of jumps
  674. that go to a RET. Just a minor convenience for myself.
  675.  
  676. ) Yeah the code is a total mess. I did not know many of the workings of VCPI
  677. and DPMI when I started. But hey, at least its functional.
  678.  
  679. ) Remember that upon reaching '_main', interrupts are still disabled. Don't
  680. forget to do the STI.
  681.  
  682. ) I hope you realize with pmode IRQ handlers, you don't have the BIOS to
  683. redirect IRQ9 to IRQ2. So any device that uses IRQ2 will actually be using 9.
  684.  
  685. ) Remember that the INT31 AX=9?? flag functions are only available in pmode.
  686. Go ahead, use the PUSHFs and POPFs in real mode to alter the IF flag... And
  687. any DPMI host that can't handle that properly deserves to crash.
  688.  
  689. ) The one byte INT3 instruction and INTO are treated as exceptions in pmode,
  690. and cause immediate termination (unless you change that in PMODE.ASM). In real
  691. mode they are sent to their real mode handlers.
  692.  
  693. ) I would REALLY suggest not ever switching your stack in protected mode
  694. yourself.
  695.  
  696. ) This thing was coded under TASM 3.0. So if you have something different,
  697. don't blame me if it doesn't compile.
  698.  
  699. ) For those of you who didn't realize it. There are no memory free functions
  700. for low and high mem because all you have to do is subtract the amount you
  701. want to free from '_lomembase' or '_himembase'.
  702.  
  703. ------------------------------------------------------------------------------
  704. Heres a list of the vars provided by PMODE to your program:
  705.  
  706. _lomembase:dword
  707.   Low mem base for allocation (first free byte).
  708.  
  709. _lomemtop:dword
  710.   Top of low mem (last free byte +1).
  711.  
  712. _himembase:dword
  713.   High mem base for allocation (first free byte).
  714.  
  715. _himemtop:dword
  716.   Top of high mem (last free byte +1).
  717.  
  718. _pspa:dword
  719.   Absolute linear address of start of PSP.
  720.  
  721. _code16a:dword
  722.   Absolute linear address of start of CODE16.
  723.  
  724. _code32a:dword
  725.   Absolute linear address of start of CODE32 (32bit code offset from this).
  726.  
  727. _selcode:word
  728.   Code segment selector.
  729.  
  730. _seldata:word
  731.   Data segment alias selector for code.
  732.  
  733. _selzero:word
  734.   Data segment starting at absolute 0.
  735.  
  736. _irqmode:word
  737.   Bitmap for all 16 IRQs (actually 15, but were ignoring 2) of how they should
  738.   be redirected to their real mode handlers (this is new to PMODE 2.1232).
  739.  
  740. _sysbyte0:byte
  741.   System bits, they have the following meanings (if I work on this system,
  742.   in the future they may also contain information on DMA maybe).
  743.   bits:
  744.     0-1: 00=raw, 01=XMS, 10=VCPI, 11=DPMI
  745.     2-7: undefined
  746.  
  747. _getirqvect:dword
  748.   A pointer to the get IRQ function appropriate for the mode.
  749.   The function takes arguments as follows:
  750.     In:
  751.       BL - IRQ num (0-0fh)
  752.     Out:
  753.       EDX - offset of current IRQ handler
  754.  
  755. _setirqvect:dword
  756.   A pointer to the set IRQ function.
  757.     In:
  758.       BL - IRQ num (0-0fh)
  759.       EDX - offset of new IRQ handler to set
  760.  
  761. ------------------------------------------------------------------------------
  762. And now some 'functions'. Remember, when calling any of them, you should have:
  763.   CS=_selcode, DS=ES=FS=_seldata, GS=_selzero, DF=0 (CLD).
  764.  
  765. _getmem:
  766.   Allocate any mem, (first cheks low, then high)
  767.   In:
  768.     EAX - size requested
  769.   Out:
  770.     CF=0 - memory allocated
  771.     CF=1 - not enough mem
  772.     EAX - linear pointer to mem or ? if not enough
  773.  
  774. _getlomem:
  775.   Allocate some low mem
  776.   In:
  777.     EAX - size requested
  778.   Out:
  779.     CF=0 - memory allocated
  780.     CF=1 - not enough mem
  781.     EAX - linear pointer to mem or ? if not enough
  782.  
  783. _gethimem:
  784.   Allocate some high mem
  785.   In:
  786.     EAX - size requested
  787.   Out:
  788.     CF=0 - memory allocated
  789.     CF=1 - not enough mem
  790.     EAX - linear pointer to mem or ? if not enough
  791.  
  792. _lomemsize:
  793.   Get amount of free low mem
  794.   Out:
  795.     EAX - number of bytes free
  796.  
  797. _himemsize:
  798.   Get amount of free high mem
  799.   Out:
  800.     EAX - number of bytes free
  801.  
  802. _getirqmask:
  803.   Get status of IRQ mask bit (at port 21h or A1h)
  804.   In:
  805.     BL - IRQ num (0-15)
  806.   Out:
  807.     AL - status: 0=enabled, 1=disabled
  808.  
  809. _setirqmask:
  810.   Set status of IRQ mask bit
  811.   In:
  812.     BL - IRQ num (0-15)
  813.     AL - status: 0=enabled, 1=disabled
  814.  
  815. _exit:
  816.   Exit to real mode
  817.  
  818. ------------------------------------------------------------------------------
  819. And now, 2.1232:
  820.  
  821.   Aside from a few typos in the doc, PMODE v1.29a was imperfect in another
  822. way. In raw/XMS mode, it executed real mode calls in V86 mode. Locking out
  823. anything in the real mode system that needed to switch to protected mode
  824. temporarily. This could be the XMS manager or a disk cache usually. And under
  825. VCPI it executed all real mode calls by giving control back to the VCPI server
  826. under all conditions. Sometimes this might not be necessary, just to execute
  827. a minor real mode routine maybe. PMODE v2.1232 allows you to control what type
  828. of real mode call is done. Whether the call is executed in V86 mode under the
  829. control of PMODE, or if the system is switched back to actual real mode (or
  830. control passed back to the server in the case of VCPI).
  831.  
  832.   I've added two new interrupts in pmode, INT 34h and 35h. Which have the same
  833. functions as INTs 32h and 33h respectively. The difference is that INT 32h and
  834. 33h are the safe way to do the real mode calls. That is they actually return
  835. to real mode (pass control back to the server under VCPI) to handle their
  836. function. INTs 34h and 35h execute the real mode calls by switching the system
  837. to V86 mode and keeping PMODE in control. This has the advantage of keeping in
  838. place all your protected mode IRQ handlers. If you do one of the safe calls,
  839. the entire protected mode system is put on hold while the call executes. INT
  840. 32h from real mode still does what it is supposed to. No matter if it executed
  841. from a safe or a V86 real mode call.
  842.  
  843.   The way IRQs are redirected to their real mode handlers can also be
  844. controlled by you. The '_irqmode' word defines how each of the 16 IRQs are
  845. redirected to real mode. Bits 0-15 stand directly for each of the 16 IRQs. A 1
  846. in an IRQ bit means that the IRQ will be redirected safely (switch to actual
  847. real mode). A 0 means the system will switch to V86 mode to do the IRQ handler
  848. under the control of PMODE. '_irqmode' starts out with all bits set (all IRQs
  849. are redirected safely).
  850.  
  851.   PMODE 2.1232 no longer reprograms the interrupt controllers for different
  852. base vectors for the hardware IRQs. PMODE 1.29a did that to relocate the low
  853. 8 IRQs from the 386 exception vectors. Since it always did real mode calls in
  854. V86 mode, it could always redirect the new vectors back to their default real
  855. mode values. But 2.1232 can actually return to real mode where the IRQs cannot
  856. be redirected through the pmode IDT. And doing 16 real mode IRQ redirectors
  857. might not be possible if VCPI remaps the vectors itself. And I don't like the
  858. idea of reprogramming the interrupt controllers every time an actual mode
  859. switch occurs (pmode/real, not pmode/V86). The way I did it now came out to be
  860. the best option in my mind. Any exceptions that are overridden by IRQs are
  861. lost. That is, any exception that has been overridden by an IRQ will be sent
  862. to that IRQ erroneously. The usual exceptions that are overridden are 8-0fh.
  863. All of which are terminal faults which you should not get in the first place.
  864. There is one exception that is treated in a special way. Exception 13 is
  865. needed for V86 real mode calls. As a result, it will always be on interrupt
  866. vector 13. Even if an IRQ overrides it (usually 5). But the IRQ will not be
  867. lost. When the handler for exception 13 gets control, it checks whether the
  868. source of an int is an IRQ or an actual exception. For you speed freaks out
  869. there, the check and redirection to IRQ handler in case of an IRQ takes 3
  870. instructions. Too bad, normally all pmode IRQ handlers get control as soon as
  871. physically possible (and appropriate to the system type (DPMI or not)).
  872.  
  873.   If you didn't entirely understand that last paragraph (and I don't blame
  874. you). Let me sum it up. You can get and set and enable and disable all 16 IRQs
  875. as usual. Except that there will usually be a 3 instruction delay on IRQ5 in
  876. getting to its handler. Exceptions 8, 9, 10, 11, 12, 14, and 15 will not make
  877. it to the exception handler. Exception 13 will though.
  878.  
  879.   Ofcourse these are all changes to the raw/XMS/VCPI side of the system. DPMI
  880. will still work exactly as it did in 1.29a. Under DPMI, there is no
  881. distinction between V86 mode and actual real mode. Thus, INTs 32h and 33h are
  882. handled in exactly the same way as INTs 34h and 35h. It comes out to be a more
  883. consistent int/IRQ system:
  884.  
  885. ) Under raw/XMS/VCPI:
  886.   ) IRQs that occur in protected mode will go directly to their protected mode
  887.     handlers, where they can be sent on to their real mode handlers or not.
  888.   ) IRQs that occur in a real mode safe call (INT32/33) will not make it to
  889.     the pmode handlers, but will go directly to their real mode handlers.
  890.   ) IRQs that occur in a real mode V86 call (INT34/35) will go on to the
  891.     pmode handlers, where they can be sent on to their real mode handlers or
  892.     not.
  893. ) Under DPMI:
  894.   ) IRQs should ALWAYS go to the protected mode handler first, then may be
  895.     sent on to their real mode handlers or not. However, DPMI implementations
  896.     out there now are far from perfect, and may not always do that. They might
  897.     separate the two IRQ systems (IRQs in real mode go ONLY to their real mode
  898.     handlers. IRQs in pmode go ONLY to their pmode handlers, and cannot be
  899.     sent on to real mode).
  900.  
  901. ------------------------------------------------------------------------------
  902. Oh well...:
  903.  
  904.   That's it for these minor additions to this doc and code. It should now be
  905. a very reliable system. The design could still be better, but whatever...
  906. PMODE.ASM is still a mess, but it is still functional. If you use this pmode
  907. system is some production, please give me credits somewhere in the thing...
  908. That's all I ask... L8r...
  909.  
  910.   Tran ... (Thomas Pytel) of Renaissance.
  911.  
  912. Greets to all my friends, and all the k00l coders of the world...
  913. Also, to all demo people (artists, muzicians, other coders, etc...)...
  914.  
  915. Thanx to Tim Sweeny for the DPMI specs, and Josh Jensen (CyberStrike) for that
  916. VCPI doc.
  917.  
  918. You can reach me on Creativity Demo Net or SBCnet as 'Tom Tran'...
  919. Or just call the Sound Barrier: (718)979-6629, (718)979-9406...
  920. Or Internet: tran@phantom.com...
  921.  
  922. I would date this document, except that I don't know todays date. But I think
  923. it is now sometime at the beginning of June 1993... Or maybe not, Who knows...
  924.  
  925.